package com.example.client_group5;

import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Stack;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Debug;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;

public class ImageManager {

	public static ImageManager imageManager = null;

	public long memoryUsed;

	// Just using a hashmap for the cache. SoftReferences would
	// be better, to avoid potential OutOfMemory exceptions
	// private HashMap<String, SoftReference<Bitmap>> imageMap = new
	// HashMap<String, SoftReference<Bitmap>>();
	private HashMap<String, Bitmap> imageMap = new HashMap<String, Bitmap>();
	private File cacheDir;
	private ImageQueue imageQueue = new ImageQueue();
	private Thread imageLoaderThread = new Thread(new ImageQueueManager());
	private Activity activity = new Activity();
	private Options options;

	public static ImageManager getInstance(Context context) {
		if (ImageManager.imageManager == null) {
			ImageManager.imageManager = new ImageManager(context);
			Log.d("imagemanager", "Tao Instance moi ....");
		}
		return ImageManager.imageManager;
	}

	private ImageManager(Context context) {
		// Make background thread low priority, to avoid affecting UI
		// performance
		imageLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);
		options = new BitmapFactory.Options();
		options.inSampleSize = 3;
		options.inTempStorage = new byte[16 * 1024];
		options.inPurgeable = true;
		options.inInputShareable = true;
		// options.inJustDecodeBounds = true;
		memoryUsed = 0;
		// Find the dir to save cached images
		String sdState = android.os.Environment.getExternalStorageState();
		if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
			// File sdDir =
			// android.os.Environment.getExternalStorageDirectory();
			File sdDir = context.getCacheDir();
			cacheDir = new File(sdDir, "data/codehenge");
		} else
			cacheDir = context.getCacheDir();

		if (!cacheDir.exists())
			cacheDir.mkdirs();
	}

	public void displayImage(String url, Activity activity,
			ImageView imageView, ProgressBar progressBar) {
		//options.inSampleSize = 3;
		this.activity = activity;
		if (imageMap.containsKey(url)) {
			// imageView.setImageBitmap(imageMap.get(url).get());
			imageView.setImageBitmap(imageMap.get(url));
			progressBar.setVisibility(View.GONE); // ADDED
			imageView.setVisibility(View.VISIBLE); // ADDED
		} else {

			queueImage(url, activity, imageView, progressBar);
			// imageView.setImageResource(R.drawable.icon);
			imageView.setVisibility(View.GONE); // ADDED
			progressBar.setVisibility(View.VISIBLE); // ADDED
		}
	}

	public void displayImage(String url, Activity activity,
			ImageView imageView, ProgressBar progressBar, boolean isViewImage){
		//if(im)
	}
	
	private void queueImage(String url, Activity activity, ImageView imageView,
			ProgressBar progressBar) {
		// This ImageView might have been used for other images, so we clear
		// the queue of old tasks before starting.
		imageQueue.Clean(imageView); // xoa queue luon :))
		Log.d("imgPush", "Url:" + url);
		ImageRef p = new ImageRef(url, imageView, progressBar);

		synchronized (imageQueue.imageRefs) {
			// imageQueue.imageRefs.add(p);
			imageQueue.imageRefs.push(p);
			imageQueue.imageRefs.notifyAll();
		}
		Log.d("queueSize", "After push:" + imageQueue.imageRefs.size());
		// Start thread if it's not started yet
		if (imageLoaderThread.getState() == Thread.State.NEW)
			imageLoaderThread.start();
	}

	private Bitmap getBitmap(String url) {
		String filename = String.valueOf(url.hashCode());
		File f = new File(cacheDir, filename);
		// Is the bitmap in our cache?
		System.gc();
		Bitmap bitmap = BitmapFactory.decodeFile(f.getPath(), options);
		// long size = bitmap.getRowBytes()*bitmap.getHeight();
		if (bitmap != null) {
			// bitmap.recycle();
			return bitmap;
		}

		// Nope, have to download it
		try {
			bitmap = BitmapFactory.decodeStream(new URL(url).openConnection()
					.getInputStream(), null, options);
			writeFile(bitmap, f);
			return bitmap;
		} catch (Exception ex) {
			ex.printStackTrace();
			return null;
		}
	}

	private void writeFile(Bitmap bmp, File f) {
		FileOutputStream out = null;

		try {
			out = new FileOutputStream(f);

			bmp.compress(Bitmap.CompressFormat.JPEG, 80, out);
			Object[] key = imageMap.keySet().toArray();
			int i = 0;
			while (cacheDir.length() > 10 * 1024 * 1024) {
				if (i < key.length) {
					String filename = String.valueOf(key[i].hashCode());
					File fn = new File(cacheDir, filename);
					if (fn.exists()) {
						if (fn.delete()) {
							Log.d("m", "Deleting file in cache ...");
						}
					}
				} else {
					break;
				}
				i++;
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (out != null)
					out.close();
			} catch (Exception ex) {
			}
		}
	}

	public static boolean checkBitmapFitsInMemory(long bmpwidth,
			long bmpheight, int bmpdensity) {
		long reqsize = bmpwidth * bmpheight * bmpdensity;
		long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
		Log.d("m", "req size:" + reqsize);
		Log.d("m", "allocNativeHeap: " + allocNativeHeap);
		Log.d("m", "max memory:" + Runtime.getRuntime().maxMemory());
		Log.d("m", "total memory:" + Runtime.getRuntime().totalMemory());
		Log.d("m", "free memory:" + Runtime.getRuntime().freeMemory());

		final long heapPad = (long) Math.max(4 * 1024 * 1024, Runtime
				.getRuntime().maxMemory() * 0.1);
		Log.d("m", "total: " + (reqsize + allocNativeHeap + heapPad));
		if ((reqsize + allocNativeHeap + heapPad) >= Runtime.getRuntime()
				.maxMemory()) {
			Log.d("m", "return false");
			return false;
		}
		Log.d("m", "return true");
		return true;

	}

	/** Classes **/

	private class ImageRef {
		public String url;
		public ImageView imageView;
		public ProgressBar progressBar;

		public ImageRef(String u, ImageView i, ProgressBar p) {
			url = u;
			imageView = i;
			progressBar = p;
		}
	}

	// stores list of images to download
	private class ImageQueue {
		private Stack<ImageRef> imageRefs = new Stack<ImageRef>();

		// removes all instances of this ImageView
		public void Clean(ImageView view) {
			for (int i = 0; i < imageRefs.size();) {
				if (imageRefs.get(i).imageView == view)
					imageRefs.remove(i);
				else
					++i;
			}

		}
	}

	private class ImageQueueManager implements Runnable {
		@Override
		public void run() {
			try {
				while (true) {
					// Thread waits until there are images in the
					// queue to be retrieved
					if (imageQueue.imageRefs.size() == 0) {
						synchronized (imageQueue.imageRefs) {
							imageQueue.imageRefs.wait();
						}
					}

					// When we have images to be loaded
					if (imageQueue.imageRefs.size() != 0) {
						ImageRef imageToLoad;

						synchronized (imageQueue.imageRefs) {
							imageToLoad = imageQueue.imageRefs.pop();
						}
						if (imageToLoad.url == null
								|| "".equalsIgnoreCase(imageToLoad.url.trim()))
							continue;
						Bitmap bmp;
						if (!imageMap.containsKey(imageToLoad.url)) {

							bmp = getBitmap(imageToLoad.url);
							Log.d("m", "url load" + imageToLoad.url);
							if (bmp == null)
								continue;

							Object[] key = imageMap.keySet().toArray();
							int i = 0;
							while (!ImageManager.checkBitmapFitsInMemory(
									bmp.getWidth(), bmp.getHeight(), 2)) {
								if (i < key.length && i < 10) {
									if (imageMap.containsKey(key[i])) {
										imageMap.remove(key[i]);
										Runtime.getRuntime().gc();
									}
									Log.d("m", "Exist Memory--- remove key:"
											+ key[i]);
								} else {
									break;
								}
								i++;
							}
							synchronized (imageMap) {
								imageMap.put(imageToLoad.url, (bmp));
							}
						} else {
							bmp = imageMap.get(imageToLoad.url);
						}

						Log.d("m", "Sized of hash: " + imageMap.size());

						Log.d("m", "total memory: "
								+ Runtime.getRuntime().totalMemory());
						Log.d("m", "Free memory: "
								+ Runtime.getRuntime().freeMemory());
						Log.d("m", "used memory: "
								+ (Runtime.getRuntime().totalMemory() - Runtime
										.getRuntime().freeMemory()));

						Object tag = imageToLoad.imageView.getTag();
						Log.d("thread", "vao day" + tag + ";" + imageToLoad.url);
						// Make sure we have the right view - thread safety
						// defender
						if (tag != null
								&& ((String) tag).equals(imageToLoad.url)) {
							BitmapDisplayer bmpDisplayer = new BitmapDisplayer(
									bmp, imageToLoad.imageView,
									imageToLoad.progressBar);
							activity.runOnUiThread(bmpDisplayer);
						}
					}

					if (Thread.interrupted())
						break;
				}
			} catch (InterruptedException e) {
			}
		}
	}
	
	// Used to display bitmap in the UI thread
	private class BitmapDisplayer implements Runnable {
		Bitmap bitmap;
		ImageView imageView;
		ProgressBar progressBar;

		public BitmapDisplayer(Bitmap b, ImageView i, ProgressBar p) {
			bitmap = b;
			imageView = i;
			progressBar = p;
		}

		public void run() {
			Log.d("thread", "vao day");
			if (bitmap != null) {

				imageView.setImageBitmap(bitmap);
				progressBar.setVisibility(View.GONE); // ADDED
				imageView.setVisibility(View.VISIBLE); // ADDED
			} else {
				imageView.setVisibility(View.GONE); // ADDED
				progressBar.setVisibility(View.VISIBLE); // ADDED
			}
		}
	}

}